package cn.test.client;

import cn.test.server.ServerHeartHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.timeout.IdleStateHandler;

import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;

/***
 *
 *
 * @Author: SJie
 * Date: 2019/12/19 下午5:52
 * Description:
 *
 */

public class ClientMain {
    String host = "127.0.0.1";
    int port = 2000;
    public static Bootstrap bootstrap;

    public void start() throws InterruptedException {
//创建生命周期组，EventLoopGroup包含一个或多个EventLoop，而EventLoop在一个生命周期内只能绑定一个Thread
        //每一个EventLoop的I/O事件都是由这个Thread处理，一个channel在生命周期内只能对应一个EventLoop，
        //但一个EventLoop可以被分给一个或多个channel，因此channel和thread是对应的
        EventLoopGroup group = null;
        try {
             bootstrap = new Bootstrap();//创建一个引导启动类
            group = new NioEventLoopGroup();
            bootstrap.group(group)//把事件生命周期组EventLoopGroup注册引导启动类中去启动
                    .channel(NioSocketChannel.class)//注册channel类型为NioSocketChannel，这个类型还有NioSctpChannel，NioDatagramChannel，LocalServerChannel，EmbeddedChannel
                    .remoteAddress(new InetSocketAddress(host, port))//注册连接的服务器地址端口
                    .option(ChannelOption.TCP_NODELAY, true)
                    .option(ChannelOption.SO_KEEPALIVE, true)
                    //注册事件操作句柄，使用childHandler时候不可以，所以只能用handler代替了
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(//初始化通道
                                                   io.netty.channel.socket.SocketChannel ch)
                                throws Exception {
                            //一个SocketChannel可以添加多个ChannelHandler，可以多加addLast，
                            //这个ChannelHandler，有两种In和Out即ChannelInboundHandler，ChannelOutboundHandler
                            //pipeline 在处理In和Out顺序是，in是从头部开始，out是尾部开始，例如 in1，out1,out2,in2,运行结果是in1->in2,out2->out1
                            //ChannelInboundHandler之间的传递，通过调用 ctx.fireChannelRead(msg) 实现；调用ctx.write(msg) 将传递到ChannelOutboundHandler，
                            //ctx.write()方法执行后，需要调用flush()方法才能令它立即执行，
                            //pipeline中outhandler不能放在最后，否则不生效
                            ch.pipeline()
//                                    .addLast("IdleStateHandler",new IdleStateHandler(0,0,5))
                                    // 自定义长度帧解码器
//                                    .addLast("frameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 1, 0, 1))
                                    // 预编码消息长度的编码器。 长度值预先作为二进制形式。
//                                    .addLast("frameEncoder", new LengthFieldPrepender(1))
//                                    .addLast(new ClientHeartHandler())
//                                    .addLast(new JdkEncoder())
//                                    .addLast(new JdkDecoder())
////                                    .addLast("decoder", new StringDecoder())
////                                    .addLast("encoder", new StringEncoder())
//                                    .addLast(new IdleStateHandler(3, 3, 0, TimeUnit.SECONDS))
//                                    .addLast(new ClientHandlerOut1())
//
                                    .addLast(new ClientHandler1())
//                                    .addLast(new ClientHandler2())
//                                    .addLast(new ClientHandler3())
//                                    .addLast(new ClientHandlerOut2())
                                    .addLast(new ClientHandlerOut3());


                        }
                    });
// 最后绑定服务器等待直到绑定完成，调用sync()方法会阻塞直到服务器完成绑定,然后服务器等待通道关闭，因为使用sync()，所以关闭操作也会被阻塞。
            ChannelFuture sync = bootstrap.connect();//引导启动连接，ChannelFuture为将要执行操作的占位符

            sync.channel().close().sync();
           // sync.channel().closeFuture().sync();//关闭占位符，而不关闭整个通道，close是关闭整个客户端
        } finally {
            //group.shutdownGracefully().sync();//关闭整个线程组
        }
    }

    public static void main(String[] args) throws InterruptedException {
        try {
            ClientMain clientMain=new ClientMain();
            clientMain.start();
            ClientMain.doconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public static void doconnect(){
        ChannelFuture future=ClientMain.bootstrap.connect();
        future.addListener(new ChannelFutureListener() {
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                if (channelFuture.isSuccess()) {
                    System.out.println("connect success");
                }else{
                    System.out.println("connect fail");
                    channelFuture.channel().eventLoop().schedule(new Runnable() {
                        public void run() {
                            System.out.println("connecting");
                            doconnect();
                        }
                    }, 5, TimeUnit.SECONDS);
                }
            }
        });
    }
}
